<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>src/lib/plugin-loader.coffee</title>
    <link rel="stylesheet" href="http://yui.yahooapis.com/3.9.1/build/cssgrids/cssgrids-min.css">
    <link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
    <link rel="stylesheet" href="../assets/css/main.css" id="site_styles">
    <link rel="icon" href="../assets/favicon.ico">
    <script src="http://yui.yahooapis.com/combo?3.9.1/build/yui/yui-min.js"></script>
</head>
<body class="yui3-skin-sam">

<div id="doc">
    <div id="hd" class="yui3-g header">
        <div class="yui3-u-3-4">
                <h1><img src="../assets/css/logo.png" title="" width="117" height="52"></h1>
        </div>
        <div class="yui3-u-1-4 version">
            <em>API Docs for: </em>
        </div>
    </div>
    <div id="bd" class="yui3-g">

        <div class="yui3-u-1-4">
            <div id="docs-sidebar" class="sidebar apidocs">
                <div id="api-list">
                    <h2 class="off-left">APIs</h2>
                    <div id="api-tabview" class="tabview">
                        <ul class="tabs">
                            <li><a href="#api-classes">Classes</a></li>
                            <li><a href="#api-modules">Modules</a></li>
                        </ul>
                
                        <div id="api-tabview-filter">
                            <input type="search" id="api-filter" placeholder="Type to filter APIs">
                        </div>
                
                        <div id="api-tabview-panel">
                            <ul id="api-classes" class="apis classes">
                                <li><a href="../classes/BasePlugin.html">BasePlugin</a></li>
                                <li><a href="../classes/Collection.html">Collection</a></li>
                                <li><a href="../classes/Docpad.html">Docpad</a></li>
                                <li><a href="../classes/docpadUtil.html">docpadUtil</a></li>
                                <li><a href="../classes/DocumentModel.html">DocumentModel</a></li>
                                <li><a href="../classes/ElementsCollection.html">ElementsCollection</a></li>
                                <li><a href="../classes/Events.html">Events</a></li>
                                <li><a href="../classes/FileModel.html">FileModel</a></li>
                                <li><a href="../classes/FilesCollection.html">FilesCollection</a></li>
                                <li><a href="../classes/MetaCollection.html">MetaCollection</a></li>
                                <li><a href="../classes/Model.html">Model</a></li>
                                <li><a href="../classes/PluginLoader.html">PluginLoader</a></li>
                                <li><a href="../classes/PluginTester.html">PluginTester</a></li>
                                <li><a href="../classes/QueryCollection.html">QueryCollection</a></li>
                                <li><a href="../classes/ScriptCollection.html">ScriptCollection</a></li>
                                <li><a href="../classes/ServerTester.html">ServerTester</a></li>
                                <li><a href="../classes/StylesCollection.html">StylesCollection</a></li>
                            </ul>
                
                
                            <ul id="api-modules" class="apis modules">
                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="yui3-u-3-4">
                <div id="api-options">
                    Show:
                    <label for="api-show-inherited">
                        <input type="checkbox" id="api-show-inherited" checked>
                        Inherited
                    </label>
            
                    <label for="api-show-protected">
                        <input type="checkbox" id="api-show-protected">
                        Protected
                    </label>
            
                    <label for="api-show-private">
                        <input type="checkbox" id="api-show-private">
                        Private
                    </label>
                    <label for="api-show-deprecated">
                        <input type="checkbox" id="api-show-deprecated">
                        Deprecated
                    </label>
            
                </div>
            
            <div class="apidocs">
                <div id="docs-main">
                    <div class="content">
<h1 class="file-heading">File: src/lib/plugin-loader.coffee</h1>

<div class="file">
    <pre class="code prettyprint linenums">
# ---------------------------------
# Requires

# Standard Library
pathUtil = require(&#x27;path&#x27;)
util = require(&#x27;util&#x27;)

# External
semver = require(&#x27;semver&#x27;)
safefs = require(&#x27;safefs&#x27;)



# ---------------------------------
# Classes

# Define Plugin Loader
###*
# The Plugin Loader class
# @class PluginLoader
# @constructor
###
class PluginLoader

	# ---------------------------------
	# Constructed

	###*
	# The DocPad Instance
	# @private
	# @property {Object} docpad
	###
	docpad: null


	###*
	# The BasePlugin Class
	# @private
	# @property {Object}
	###
	BasePlugin: null


	###*
	# The full path of the plugin&#x27;s directory
	# @private
	# @property {String}
	###
	dirPath: null


	# ---------------------------------
	# Loaded

	###*
	# The full path of the plugin&#x27;s package.json file
	# @private
	# @property {String}
	###
	packagePath: null

	###*
	# The parsed contents of the plugin&#x27;s package.json file
	# @private
	# @property {Object}
	###
	packageData: {}

	###*
	# The full path of the plugin&#x27;s main file
	# @private
	# @property {String}
	###
	pluginPath: null


	###*
	# The parsed content of the plugin&#x27;s main file
	# @private
	# @property {Object}
	###
	pluginClass: {}

	###*
	# The plugin name
	# @private
	# @property {String}
	###
	pluginName: null

	###*
	# The plugin version
	# @private
	# @property {String}
	###
	pluginVersion: null

	###*
	# Node modules path
	# @private
	# @property {String}
	###
	nodeModulesPath: null


	# ---------------------------------
	# Functions

	###*
	# Constructor method
	# @method constructor
	# @param {Object} opts
	# @param {Object} opts.docpad The docpad instance that we are loading plugins for
	# @param {String} opts.dirPath The directory path of the plugin
	# @param {Object} opts.BasePlugin The base plugin class
	###
	constructor: ({@docpad,@dirPath,@BasePlugin}) -&gt;
		# Prepare
		docpad = @docpad

		# Apply
		@pluginName = pathUtil.basename(@dirPath).replace(/^docpad-plugin-/,&#x27;&#x27;)
		@pluginClass = {}
		@packageData = {}
		@nodeModulesPath = pathUtil.resolve(@dirPath, &#x27;node_modules&#x27;)


	###*
	# Loads the package.json file and extracts the main path
	# next(err,exists)
	# @method exists
	# @param {Function} next
	###
	exists: (next) -&gt;
		# Prepare
		packagePath = @packagePath or pathUtil.resolve(@dirPath, &quot;package.json&quot;)
		failure = (err=null) -&gt;
			return next(err, false)
		success = -&gt;
			return next(null, true)

		# Check the package
		safefs.exists packagePath, (exists) =&gt;
			return failure()  unless exists

			# Apply
			@packagePath = packagePath

			# Read the package
			safefs.readFile packagePath, (err,data) =&gt;
				return failure(err)  if err

				# Parse the package
				try
					@packageData = JSON.parse data.toString()
				catch err
					return failure(err)
				finally
					return failure()  unless @packageData

				# Extract the version and main
				pluginVersion = @packageData.version
				pluginPath = @packageData.main and pathUtil.join(@dirPath, @packageData.main)

				# Check defined
				return failure()  unless pluginVersion
				return failure()  unless pluginPath

				# Success
				@pluginVersion = pluginVersion
				@pluginPath = pluginPath
				return success()

		# Chain
		@

	###*
	# Check if this plugin is unsupported
	# Boolean value returned as a parameter
	# in the passed callback
	# next(err,supported)
	# @method unsupported
	# @param {Function} next
	###
	unsupported: (next) -&gt;
		# Prepare
		docpad = @docpad

		# Extract
		version = @packageData.version
		keywords = @packageData.keywords or []
		platforms = @packageData.platforms or []
		engines = @packageData.engines or {}
		peerDependencies = @packageData.peerDependencies or {}

		# Check
		unsupported =
			# Check type
			if &#x27;docpad-plugin&#x27; not in keywords
				&#x27;type&#x27;

			# Check version
			else if version and not semver.satisfies(version, docpad.pluginVersion)
				&#x27;version-plugin&#x27;

			# Check platform
			else if platforms.length and process.platform not in platforms
				&#x27;platform&#x27;

			# Check node engine
			else if engines.node? and not semver.satisfies(process.version, engines.node)
				&#x27;engine-node&#x27;

			# Check docpad engine
			else if engines.docpad? and not semver.satisfies(docpad.getVersion(), engines.docpad)
				&#x27;version-docpad&#x27;

			# Check docpad peerDependencies
			else if peerDependencies.docpad? and not semver.satisfies(docpad.getVersion(), peerDependencies.docpad)
				&#x27;version-docpad&#x27;

			# Supported
			else
				false

		# Supported
		next(null, unsupported)

		# Chain
		@

	###*
	# Installs the plugins node modules.
	# next(err)
	# @private
	# @method install
	# @param {Function} next
	###
	install: (next) -&gt;
		# Prepare
		docpad = @docpad

		# Only install if we have a package path
		if @packagePath
			# Install npm modules
			docpad.initNodeModules(
				path: @dirPath
				next: (err,results) -&gt;
					# Forward
					return next(err)
			)
		else
			# Continue
			next()

		# Chain
		@

	###*
	# Load in the pluginClass from the plugin file.
	# The plugin class that has been loaded is returned
	# in the passed callback
	# next(err,pluginClass)
	# @method load
	# @param {Function} next
	###
	load: (next) -&gt;
		# Prepare
		docpad = @docpad
		locale = docpad.getLocale()

		# Ensure we still have deprecated support for old-style uncompiled plugins
		if pathUtil.extname(@pluginPath) is &#x27;.coffee&#x27;
			# Warn the user they are trying to include an uncompiled plugin (if they want to be warned)
			# They have the option of opting out of warnings for private plugins
			unless @packageData.private is true and docpad.getConfig().warnUncompiledPrivatePlugins is false
				docpad.warn util.format(locale.pluginUncompiled, @pluginName, @packageData.bugs?.url or locale.pluginIssueTracker)

			# Attempt to include the coffee-script register extension
			# coffee-script is an external party dependency (docpad doesn&#x27;t depend on it, so we don&#x27;t install it)
			# so we may not have it, hence the try catch
			try
				require(&#x27;coffee-script/register&#x27;)
			catch err
				# Including coffee-script has failed, so let the user know, and exit
				err.context = util.format(locale.pluginUncompiledFailed, @pluginName, @packageData.bugs?.url or locale.pluginIssueTracker)
				return next(err); @


		# Attempt to load the plugin
		try
			@pluginClass = require(@pluginPath)(@BasePlugin)
		catch err
			# Loading the plugin has failed, so let the user know, and exit
			err.context = util.format(locale.pluginLoadFailed, @pluginName, @packageData.bugs?.url or locale.pluginIssueTracker)
			return next(err); @

		# Plugin loaded, inject it&#x27;s version and grab its name
		@pluginClass::version ?= @pluginVersion
		pluginPrototypeName = @pluginClass::name

		# Check Alphanumeric Name
		if /^[a-z0-9]+$/.test(@pluginName) is false
			validPluginName = @pluginName.replace(/[^a-z0-9]/,&#x27;&#x27;)
			docpad.warn util.format(locale.pluginNamingConventionInvalid, @pluginName, validPluginName)

		# Check for Empty Name
		if pluginPrototypeName is null
			@pluginClass::name = @pluginName
			docpad.warn util.format(locale.pluginPrototypeNameUndefined, @pluginName)

		# Check for Same Name
		else if pluginPrototypeName isnt @pluginName
			docpad.warn util.format(locale.pluginPrototypeNameDifferent, @pluginName, pluginPrototypeName)

		# Return our plugin
		next(null, @pluginClass)

		# Chain
		@

	###*
	# Create an instance of a plugin
	# defined by the passed config.
	# The plugin instance is returned in
	# the passed callback.
	# next(err,pluginInstance)
	# @method create
	# @param {Object} config
	# @param {Function} next
	###
	create: (config,next) -&gt;
		# Load
		try
			# Create instance with merged configuration
			docpad = @docpad
			pluginInstance = new @pluginClass({docpad,config})
		catch err
			# An error occured, return it
			return next(err, null)

		# Return our instance
		return next(null, pluginInstance)

		# Chain
		@


# ---------------------------------
# Export
module.exports = PluginLoader

    </pre>
</div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script src="../assets/vendor/prettify/prettify-min.js"></script>
<script>prettyPrint();</script>
<script src="../assets/js/yui-prettify.js"></script>
<script src="../assets/../api.js"></script>
<script src="../assets/js/api-filter.js"></script>
<script src="../assets/js/api-list.js"></script>
<script src="../assets/js/api-search.js"></script>
<script src="../assets/js/apidocs.js"></script>
</body>
</html>
